﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using org.alex.edss;

namespace TcpDownServer
{
    class DownloadSession
    {
        public HandShake Handshake
        {
            get;
            set;
        }

        public MemoryStream Link
        {
            get;
            set;
        }
        public MemoryStream Cmd
        {
            get;
            set;
        }

        public string QQ
        {
            get;
            set;
        }

        public Command PathCommand
        {
            get;
            set;
        }
        public DownloadSession()
        {
            Handshake = new HandShake();
            PathCommand = new Command();
            Link = new MemoryStream();
            Cmd = new MemoryStream();
        }

        public SessionState State
        {
            get;
            set;
        }



        public class HandShake
        {
            public int C1_Len = 16;
            public byte[] C1 = new byte[16];
            public byte[] S1 = new byte[2];
            public int Sum;
            public HandShake()
            {
                S1[0] = (byte)new Random().Next(127);
                S1[1] = (byte)new Random().Next(127);
                Sum = S1[0] + S1[1];
            }
        }
        public class Command
        {
            public bool CommandReady(byte[] data)
            {
                if (data.Length > 1)
                {
                    if (data[data.Length - 2] == '\r' && data[data.Length - 1] == '\n')
                    {
                        return true;
                    }
                    return false;
                }
                else
                {
                    return false;
                }
            }

            public DirectoryInfo RootDir
            {
                get;
                set;
            }
            public DirectoryInfo CurrentDir
            {
                get;
                set;
            }

            public bool CanDown
            {
                get;
                set;
            }
            public string DownPath
            {
                get;
                set;
            }
            public int Pos
            {
                get;
                set;
            }

            public long dFileLength;

            public long FileReadCount = 0;

            public bool ReadDone = false;

            public void Init(string path)
            {
                RootDir = new DirectoryInfo(path);
                CurrentDir = new DirectoryInfo(path);
                FileBuffer = new byte[4096];
                ReadTemp = new MemoryStream(4096);
            }

            public string DoCommand(byte[] data)
            {
                string Rs = string.Empty;
                string Command = string.Empty;
                string Param1 = string.Empty;
                string Param2 = string.Empty;
                string cmd = Encoding.UTF8.GetString(data).Replace("\r\n", "");
                string[] temp = cmd.Split(' ');
                if (temp.Length > 0)
                    Command = temp[0];
                if (temp.Length > 1)
                    Param1 = temp[1];
                if (temp.Length > 2)
                    Param2 = temp[2];
                switch (Command)
                {
                    case "ls": Rs = ls(); break;
                    case "cd": Rs = cd(Param1); break;
                    case "get":Rs=get(Param1,Param2) ;break;
                    default:
                        Rs = "no such command"; break;
                }
                return Rs;
            }

            public string get(string filename, string startPos)
            {
                int p = 0;
                if (int.TryParse(startPos, out p))
                {
                    Pos = p;
                }
                foreach (FileInfo f in CurrentDir.GetFiles())
                {
                    if (f.Name == filename)
                    {
                        this.CanDown = true;
                        this.Pos = p;
                        this.DownPath = f.FullName;
                        return "0";
                    }
                }
                return "error:no such file";
            }

            public string cd(string target)
            {
                if (target == @"\")
                {
                    CurrentDir = RootDir;
                    return "success";
                }
                if (target == "..")
                {
                    if (!CurrentDir.Equals(RootDir))
                    {
                        CurrentDir = CurrentDir.Parent;
                        return "success";
                    }
                    return "already in the root";
                }
                foreach (DirectoryInfo dir in CurrentDir.GetDirectories())
                {
                    if (dir.Name == target)
                    {
                        CurrentDir = dir;
                        return "success";
                    }
                }
                return "no such directory";
            }

            public byte[] FileBuffer;

            public MemoryStream ReadTemp;

            public void Down(Session ses)
            {
                Logger.Debug("start down file :" + DownPath);
                this.ReadDone = false;
                FileInfo fi = new FileInfo(this.DownPath);
                this.dFileLength = fi.Length;
                
                FileStream fs = new FileStream(this.DownPath, FileMode.Open);
                if (Pos > 0)
                {
                    fs.Seek(Pos, SeekOrigin.Begin);
                    this.dFileLength -= Pos;
                }
                ses.UserData["fs"] = fs;
                IAsyncResult rs = fs.BeginRead(FileBuffer, 0, 4096,
                    (AsyncCallback)delegate(IAsyncResult Ar)
                {
                    Logger.Debug("begin send file" + fi.FullName);
                    SubDown(Ar,ses);
                }, fs);
            }

            public void SubDown(IAsyncResult Ar,Session ses)
            {
                DownloadSession ds = (DownloadSession)ses.UserData["data"];
                if (ds.State == DownloadSession.SessionState.StartDown || ds.State == DownloadSession.SessionState.SendOK)
                {
                    try
                    {
                        if (Ar.IsCompleted)
                        {
                            FileStream fst = (FileStream)Ar.AsyncState;
                            int Rd = fst.EndRead(Ar);
                            FileReadCount += Rd;
                            ReadTemp.SetLength(0);
                            ds.State = DownloadSession.SessionState.ReadOk;
                            ReadTemp.Write(FileBuffer, 0, Rd);
                            FileBuffer = new byte[4096];
                            if (FileReadCount == dFileLength)
                            {
                                fst.Close();
                                Logger.Debug("file send ok");
                                this.ReadDone = true;
                            }
                            else
                            {
                                this.ReadDone = false;
                            }
                            //Logger.Debug("Send " + Rd);
                            ses.Connection.Write(ReadTemp.ToArray());
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Debug("216:SubDown" + ex.Message);
                        ses.SetError();
                    }
                }
                else
                {
                    Logger.Debug("error state:" + ses.Key + "|" + ds.State.ToString());
                    SubDown(Ar, ses);
                }
            }

            public string ls()
            {
                StringBuilder sb = new StringBuilder();
                foreach (DirectoryInfo dir in CurrentDir.GetDirectories())
                {
                    sb.Append("d ").Append(dir.Name).Append(" 0\r\n");
                }
                foreach (FileInfo f in CurrentDir.GetFiles())
                {
                    sb.Append("f ").Append(f.Name).Append(" ").Append(f.Length).Append("\r\n");
                }
                return sb.ToString();
            }
        }
        public enum SessionState
        {
            Init = 0,
            GetC1 = 1,
            SendS1 = 2,
            GetC2 = 3,
            Connected = 4,
            GetCommand = 5,
            ProcessCommand = 6,
            CommandDone = 7,
            StartDown = 8,
            Downing = 9,
            ReadOk = 10,
            Sended = 11,
            SendOK = 12,
            WaitDown = 13,
            WaitRead = 14
        }
    }
}
